package top.wilsonlv.jaguar.security.util;

import cn.hutool.core.codec.Base64;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationConverter;
import org.springframework.util.StringUtils;
import top.wilsonlv.jaguar.commons.web.exception.impl.CheckedException;
import top.wilsonlv.jaguar.commons.web.util.WebUtil;
import top.wilsonlv.jaguar.security.model.SecurityAuthority;
import top.wilsonlv.jaguar.security.model.SecurityUser;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author lvws
 * @since 2021/6/24
 */
@Slf4j
public final class SecurityUtil {

    public static Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }

    public static SecurityUser getCurrentUser() {
        return getCurrentUser(true);
    }

    public static SecurityUser getCurrentUser(boolean throwException) {
        SecurityUser securityUser = null;

        Authentication authentication = getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            Object principal = authentication.getPrincipal();
            if (principal instanceof SecurityUser) {
                securityUser = (SecurityUser) principal;
            }
        }

        if (throwException && securityUser == null) {
            throw new CheckedException("匿名用户");
        }
        return securityUser;
    }

    public static Long getCurrentUserId() {
        return getCurrentUser().getId();
    }

    public static Set<String> getCurrentUserAuthorities() {
        return getCurrentUser().getAuthorities().stream().map(SecurityAuthority::getAuthority).collect(Collectors.toSet());
    }

    public static String getClientId() {
        HttpServletRequest request = WebUtil.getRequest();
        if (request == null) {
            return null;
        }

        String header = request.getHeader(HttpHeaders.AUTHORIZATION);
        return extractClientId(header);
    }

    public static void assertCurrentUser(Long userId) {
        SecurityUser currentUser = getCurrentUser();
        if (currentUser == null || !currentUser.getId().equals(userId)) {
            throw new InsufficientAuthenticationException(null);
        }
    }

    /**
     * 解析clientId
     *
     * @param header 请求头
     * @return clientId
     */
    public static String extractClientId(String header) {
        if (!StringUtils.hasText(header) || !header.startsWith(BasicAuthenticationConverter.AUTHENTICATION_SCHEME_BASIC)) {
            log.debug("请求头中client信息为空: {}", header);
            return null;
        }

        byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
        byte[] decoded;
        try {
            decoded = Base64.decode(base64Token);
        } catch (IllegalArgumentException e) {
            log.debug("Failed to decode basic authentication token: {}", header);
            return null;
        }

        String token = new String(decoded, StandardCharsets.UTF_8);

        int colon = token.indexOf(":");
        if (colon == -1) {
            log.debug("Invalid basic authentication token: {}", header);
            return null;
        }
        return token.substring(0, colon);
    }

}
